package server;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.Hashtable;

import ocsf.server.ConnectionToClient;

import logic.Activity;
import logic.Book;
import logic.Loan;
import logic.Member;
import logic.Message;
import GUI.Client_GUI;
import GUI.ResultDisplay;

public class ReportController {
	private DB_Manager DB;
	private OBL_Server parent;
	
	
	public ReportController(){
		//empty constructor for testing with JUnit
	}
	
	public ReportController(OBL_Server parent,DB_Manager DB) {
		this.parent = parent;
		this.DB = DB;
		
	}

	
	
	/**
	 * This function calculates the statistics for the activity reports.
	 * receives an ArrayList<Integer> with data input, and returns the AraryList<Object>
	 * where the first index is the average, and the second is the median
	 * @param input - the ArrayList<Integer> with tht containing data
	 * @return ArrayList<Object> [0] - average, [1] - median
	 */
	public ArrayList<Object> ComputeStatistics(ArrayList<Integer> input){
		
		float average;
		int median;
		
		int sum = 0;
		for(int runner : input){
			sum += runner;
		}
		
		if (input.size() != 0) average = ((float)sum/input.size());
		else average = 0;
		
		Collections.sort(input);
		
		if (input.size() != 0)	median = input.get(input.size()/2);
		else median = 0;
		
		ArrayList<Object> result = new ArrayList<Object>();
		result.add(average);
		result.add(median);
		
		return result;
		
	}
	
	
	
	
	
	

	public void GetActivitiyReport(ConnectionToClient client)
	{
		String Querry= "SELECT * FROM activity;";
		ArrayList<Activity> reportList =  SeralizeResultSet(DB.Query(Querry));
				
		ArrayList<String> index = new ArrayList<String>();
		index.add("INDICATOR");
		index.add("RESULTSET");
		ArrayList<Object> data = new ArrayList<Object>();
		data.add(ResultDisplay.REPORT);
		data.add(reportList);
		
		int action = Message.SEARCH_MEMBER_RESPONSE;
		
		Message toSend = new Message(index, data, action);
		
		try{
			client.sendToClient(toSend);
		}catch(IOException e){
			parent.Log("[SearchMember] IOException. " + e.getMessage());
		}catch(NullPointerException nul){
			parent.Log("[SearchMember] NullPointerException. " + nul.getMessage());
		}	
	}
	
	
public ArrayList<Activity> SeralizeResultSet(ResultSet rs){
		
		ArrayList<Activity> ActivityList = new ArrayList<Activity>();

		try{
			while(rs.next()){
				// AID, REPORT_DATE, ACTIVE_MEMBERS, FROZEN_MEMBERS, INACTIVE_MEMBERS, TOTAL_COPIES, LOANED_COPIES, LATE_RETURN_MEMBERS
				//int TOTAL_COPIES, int LOANED_COPIES,int LATE_RETURN_MEMBERS)
				Activity Activ = new Activity(rs.getInt("AID"),rs.getString("REPORT_DATE"),rs.getInt("ACTIVE_MEMBERS"),rs.getInt("FROZEN_MEMBERS"),rs.getInt("INACTIVE_MEMBERS"),
						rs.getInt("TOTAL_COPIES"),rs.getInt("LOANED_COPIES"),rs.getInt("LATE_RETURN_MEMBERS"));
				ActivityList.add(Activ);
			}
			
			return ActivityList;
		}catch(SQLException e){
			//TODO handle seralize error
			return null;
		}
	}
	
	
	
	
	public void BuildActivityReports(){
		
		BookController bookController = new BookController(parent, DB);
		MemberController memberController = new MemberController(parent, DB);
		LoanController loanController = new LoanController(parent, DB);
		
		int active = memberController.GetNumberOfStudentsForStauts(Member.VALID_MEMBERCARD);
		int frozen = memberController.GetNumberOfStudentsForStauts(Member.FROZEN_MEMBERCARD);
		int inactive = memberController.GetNumberOfStudentsForStauts(Member.GRADUATED_MEMBERCARD);
		int availableCopies = bookController.GetNumberOfCopiesForStatus(Book.BOOK_COPY_AVAILABLE);
		int notavailable = bookController.GetNumberOfCopiesForStatus(Book.BOOK_COPY_NOT_AVAILABLE);
		
		Calendar currentTime = Calendar.getInstance();
		String currDate = currentTime.get(Calendar.YEAR) + "-";
		if (currentTime.get(Calendar.MONTH) < 10) currDate += "0" + (currentTime.get(Calendar.MONTH) + 1) + "-";
		else currDate += (currentTime.get(Calendar.MONTH) + 1) + "-";
		if (currentTime.get(Calendar.DAY_OF_MONTH) < 10) currDate += "0" + currentTime.get(Calendar.DAY_OF_MONTH);
		else currDate += currentTime.get(Calendar.DAY_OF_MONTH);
		
		Calendar startTime = Calendar.getInstance();
		startTime.add(Calendar.MILLISECOND, (int)-Scheduler.WEEK_IN_IN_MILLIES_PRESENTATION);
		String startDate = startTime.get(Calendar.YEAR) + "-";
		if (startTime.get(Calendar.MONTH) < 10) startDate += "0" + (startTime.get(Calendar.MONTH) + 1) + "-";
		else startDate += (startTime.get(Calendar.MONTH) + 1) + "-";
		if (startTime.get(Calendar.DAY_OF_MONTH) < 10) startDate += "0" + startTime.get(Calendar.DAY_OF_MONTH);
		else startDate += startTime.get(Calendar.DAY_OF_MONTH);
		
		ArrayList<Loan> loanList = loanController.GetLateLoansForDates(startDate, currDate, "");
		int latereturns = 0;
		Hashtable<Integer,Integer> temp = new Hashtable<Integer,Integer>();
		for(Loan loan : loanList){
			if (!temp.containsKey(loan.getBID())){
				latereturns++;
				temp.put(loan.getBID(), 1);
			}
		}


		String insert = "INSERT INTO activity (AID,REPORT_DATE,ACTIVE_MEMBERS,FROZEN_MEMBERS,INACTIVE_MEMBERS" +
				",TOTAL_COPIES,LOANED_COPIES,LATE_RETURN_MEMBERS) VALUES" +
				"(null,'" + currDate + "','" + active + "','" + frozen + "','" + inactive + "','" + (availableCopies + notavailable)
				+ "','" + notavailable + "','" + latereturns + "')";
		DB.Update(insert);
		
	}

	
	
	
	
	/**
	 * This function wraps the LoanReport function.
	 * It build the ArrayList<Loan> that will be computed there
	 * @param msg - the Message object containing startDate and endDate
	 * @param client - the client which requested the report
	 */
	public void CreateLoanReport(Message msg,ConnectionToClient client){

		ArrayList<Object> data = msg.GetMessageData();
		
		LoanController loanController = new LoanController(parent, DB);
		String startDate = (String)data.get(0);
		String endDate = (String)data.get(1);

		ArrayList<Loan> loanList = loanController.GetLoansForDates(startDate, endDate);
		CompuLoanReport(loanList, client);
	}
	
	/**
	 * This function wraps the LateReturnReport function
	 * it builds the ArrayList<Loan> that will be computed there
	 * @param msg - the Message object containing the startDate and endDate
	 * @param client - the client which requested the report
	 */
	public void CreateLateReturnReport(Message msg, ConnectionToClient client){
		
		ArrayList<Object> data = msg.GetMessageData();
		
		LoanController loanController = new LoanController(parent, DB);
		String startDate = (String)data.get(0);
		String endDate = (String)data.get(1);
		
		ArrayList<Loan> loanList = loanController.GetLateLoansForDates(startDate, endDate,"");
		ComputLatereturnReport(loanList, client, Message.LATE_RETURN_REPORT_RESPONSE,startDate,endDate,null);		
	}
	
	
	/**
	 * This function wraps the LateReturnReport function by category
	 * it builds the ArrayList<Loan> that will be computed there
	 * @param msg - the Message object containing the startDate,endDate & the category
	 * @param client - the client which requested the report
	 */
	public void CreateLateReturnReportByCategory(Message msg, ConnectionToClient client){
		ArrayList<Object> data = msg.GetMessageData();
		
		String selectedCategory = (String)data.get(0);
		BookController bookController = new BookController(parent, DB);
		LoanController loanController = new LoanController(parent, DB);
		String startDate = (String)data.get(1);
		String endDate = (String)data.get(2);
		
		ArrayList<Loan> tempList = loanController.GetLateLoansForDates(startDate, endDate,"");
		ArrayList<Loan> loanList = new ArrayList<Loan>();
		
		for(Loan loan : tempList){
			if (bookController.isBookInCategory(loan.getBID(), selectedCategory))
				loanList.add(loan);
		}
		
		ComputLatereturnReport(loanList, client, Message.LATE_RETURN_REPORT_CATEGORY_RESPONSE,startDate,endDate,selectedCategory);		
	}
	
	public void CreateLateReturnReportByBook(Message msg, ConnectionToClient client){
		ArrayList<Object> data = msg.GetMessageData();
		
		String BID = (String)data.get(0);
		BookController bookController = new BookController(parent, DB);
		LoanController loanController = new LoanController(parent, DB);
		String startDate = (String)data.get(1);
		String endDate = (String)data.get(2);
		
		ArrayList<Loan> loanList = loanController.GetLateLoansForDates(startDate, endDate," AND BID = " + BID);
		
		String name = bookController.getNameForBook(BID);
		
		ComputLatereturnReport(loanList, client, Message.LATE_RETURN_REPORT_BOOK_RESPONSE,
				startDate,endDate,name);	
		
	}
		
		
	
	
	
	/**
	 * this function gets all loans and calculate the loan report data
	 * and send it to the GUI department for presenting
	 * @param Loans -array of loans matching request
	 * @param client -Client which cask for the report
	 * 
	 */
	public void CompuLoanReport(ArrayList<Loan> Loans,ConnectionToClient client)
	{
		
		ArrayList<Integer>  Hot_Book = new ArrayList<Integer>();
		ArrayList<Integer>  Reg_Book = new ArrayList<Integer>();
		ArrayList<String>  C_Hot_Book = new ArrayList<String>();
		ArrayList<String>  C_Reg_Book = new ArrayList<String>();
		ArrayList<String>  both_data = new ArrayList<String>();

		int Sum1=0,Sum2=0,med_hot,med_reg;
		int count0=0,count1=0,count2=0,count3=0,count4=0,count5=0,temp=0;
		float avrgHot,avrgReg;
		
		Integer days;
		String start;
		String end;
		//TODO CAST FROM INT TO INTEGER
		
		for(Loan loan : Loans){
			start=loan.getLoanDate();
			//TODO CHANGE TO EXUAL RETUN DATE
			end=loan.getActualReturnDate();
			
				
			int year1 = Integer.parseInt(start.substring(0, 4));
			int month1 = Integer.parseInt(start.substring(5, 7));
			int day1 = Integer.parseInt(start.substring(8, 10));
			int year2 = Integer.parseInt(end.substring(0, 4));
			int month2 = Integer.parseInt(end.substring(5, 7));
			int day2 = Integer.parseInt(end.substring(8, 10));
			Calendar cal1 = new GregorianCalendar();
			Calendar cal2 = new GregorianCalendar();

			cal1.set(year1, month1-1, day1); 
			cal2.set(year2, month2-1, day2); 
			days=(int)( (cal2.getTime().getTime() - cal1.getTime().getTime()) / (1000 * 60 * 60 * 24));
			if (loan.getHotBook()==1){
				Hot_Book.add(days);
			}else{
				Reg_Book.add(days);
			}
		}
		
		for(int i=0;i<Hot_Book.size();i++){
			Sum1+=Hot_Book.get(i);
		}
		
		if (Hot_Book.size() > 0) avrgHot=Sum1/Hot_Book.size();
		else avrgHot = 0;
		
		for(int i=0;i<Reg_Book.size();i++){
			Sum2+=Reg_Book.get(i);
		}
		if (Reg_Book.size() > 0) avrgReg=Sum2/Reg_Book.size();
		else avrgReg = 0;

		Collections.sort(Hot_Book);
		Collections.sort(Reg_Book);
		if (Hot_Book.size() > 0) med_hot=Hot_Book.get(Hot_Book.size()/2);
		else med_hot = 0;
		if (Reg_Book.size() > 0) med_reg=Reg_Book.get(Reg_Book.size()/2);
		else med_reg = 0;
		for(int i=0;i<Hot_Book.size();i++)
		{
			temp= Hot_Book.get(i);
			if(temp<=0) count1++;
			else if (temp==1) count2++;
				else if (temp==2) count3++;
					 else if (temp==3) count4++;
					 	  else count5++;
		}
		//TODO add books that not on the list
		float t1;
		float t2;
		float t3;
		float t4;
		float t5;
		if (Hot_Book.size() > 0){
			t1=((count1/(Hot_Book.size()))*100);
			t2=((count2/(Hot_Book.size()))*100);
			t3=((count3/(Hot_Book.size()))*100);
			t4=((count4/(Hot_Book.size()))*100);
			t5=((count5/(Hot_Book.size()))*100);
		}else{
			t1=0;
			t2=0;
			t3=0;
			t4=0;
			t5=0;
		}

		C_Hot_Book.add(String.valueOf(t1));
		C_Hot_Book.add(String.valueOf(t2));
		C_Hot_Book.add(String.valueOf(t3));
		C_Hot_Book.add(String.valueOf(t4));
		C_Hot_Book.add(String.valueOf(t5));
		count1=0;
		count2=0;
		count3=0;
		count4=0;
		count5=0;
		for(int i=0;i<Reg_Book.size();i++)
		{
			temp= Reg_Book.get(i);
			if(temp<=3) count1++;
			else if (temp<=7) count2++;
				else if (temp<=10) count3++;
					 else if (temp<=14) count4++;
					 	  else count5++;
		}
		//TODO add books that not on the list
		if (Reg_Book.size() > 0){
			t1=((count1/(Reg_Book.size()))*100);
			t2=((count2/(Reg_Book.size()))*100);
			t3=((count3/(Reg_Book.size()))*100);
			t4=((count4/(Reg_Book.size()))*100);
			t5=((count5/(Reg_Book.size()))*100);
		}else{
			t1=0;
			t2=0;
			t3=0;
			t4=0;
			t5=0;
		}

		C_Reg_Book.add(String.valueOf(t1));
		C_Reg_Book.add(String.valueOf(t2));
		C_Reg_Book.add(String.valueOf(t3));
		C_Reg_Book.add(String.valueOf(t4));
		C_Reg_Book.add(String.valueOf(t5));
		both_data.add(String.valueOf(avrgHot));
		both_data.add(String.valueOf(med_hot));
		both_data.add(String.valueOf(avrgReg));
		both_data.add(String.valueOf(med_reg));
		
		//TODO SEND 3 array c_hot c_reg  data
		
			
		ArrayList<String> index = new ArrayList<String>();
		ArrayList<Object> data = new ArrayList<Object>();
		index.add("STAT_HOT");
		data.add(C_Hot_Book);
		index.add("STST_REG");
		data.add(C_Reg_Book);
		index.add("DATA");
		data.add(both_data);
		
		
		int action = Message.LOAN_REPORT_RESPONSE;
		
		Message toSend = new Message(index, data, action);
		
		try{
			client.sendToClient(toSend);
		}catch(IOException e){
			parent.Log("[ComputLatereturnReport] IOException. " + e.getMessage());
		}
	}
	
	

	
	/**
	 * 
	 * @param Loans -array of loans matching request
	 * @param client-Client which cask for the report
	 * @param action - the required action from this fucntion
	 * @param startDate - the report start date
	 * @param endDate - the report end date
	 * @param name - the title name to be displayed on the reports GUI menu
	 */
	public void ComputLatereturnReport(ArrayList<Loan> Loans,ConnectionToClient client,int action,
			String startDate,String endDate,String name)
	{
		ArrayList<Integer>  Book = new ArrayList<Integer>();
		ArrayList<String>  Statics_Book = new ArrayList<String>();
		int Sum=0,medrian;
		int count1=0,count2=0,count3=0,count4=0,count5=0,temp=0;
		float avrg;
		
		Integer days;
		String start;
		String end;
		
		for(Loan loan : Loans){
			start=loan.getReturnDate();
			end=loan.getActualReturnDate();
			
				
			int year1 = Integer.parseInt(start.substring(0, 4));
			int month1 = Integer.parseInt(start.substring(5, 7));
			int day1 = Integer.parseInt(start.substring(8, 10));
			int year2 = Integer.parseInt(end.substring(0, 4));
			int month2 = Integer.parseInt(end.substring(5, 7));
			int day2 = Integer.parseInt(end.substring(8, 10));
			Calendar cal1 = new GregorianCalendar();
			Calendar cal2 = new GregorianCalendar();

			cal1.set(year1, month1-1, day1); 
			cal2.set(year2, month2-1, day2); 
			days=(int)( (cal1.getTime().getTime() - cal2.getTime().getTime()) / (1000 * 60 * 60 * 24));
			Book.add(days);
				
		}
		for(int i=0;i<Book.size();i++){
			Sum+=Book.get(i);
		}
		
		if (Book.size() > 0) avrg=Sum/Book.size();
		else avrg = 0;
		Collections.sort(Book);
		
		if (Book.size() > 0) medrian=Book.get(Book.size()/2);
		else medrian = 0;
		for(int i=0;i<Book.size();i++){
			temp= Book.get(i);
			if(temp<=1) count1++;
			else if (temp==2) count2++;
				else if (temp==3) count3++;
					 else if (temp==4) count4++;
					 	  else count5++;
		}
		//TODO add books that not on the list
		float t1;
		float t2;
		float t3;
		float t4;
		float t5;
		
		if(Book.size() > 0){
			t1=((count1/(Book.size()))*100);
			t2=((count2/(Book.size()))*100);
			t3=((count3/(Book.size()))*100);
			t4=((count4/(Book.size()))*100);
			t5=((count5/(Book.size()))*100);
		}else{
			t1=0;
			t2=0;
			t3=0;
			t4=0;
			t5=0;
		}

		Statics_Book.add(String.valueOf(t1));
		Statics_Book.add(String.valueOf(t2));
		Statics_Book.add(String.valueOf(t3));
		Statics_Book.add(String.valueOf(t4));
		Statics_Book.add(String.valueOf(t5));

		ArrayList<String> index = new ArrayList<String>();
		ArrayList<Object> data = new ArrayList<Object>();
		index.add("STATICS");
		data.add(Statics_Book);
		index.add("AVRAGE");
		data.add(String.valueOf(avrg));
		index.add("MEDRIAN");
		data.add(String.valueOf(medrian));
		index.add("STARTDATE");
		data.add(startDate);
		index.add("ENDDATE");
		data.add(endDate);
		index.add("CATEGORY");
		data.add(name);
		
		Message toSend = new Message(index, data, action);
		
		try
		{
			client.sendToClient(toSend);
		}catch(IOException e){
			parent.Log("[ComputLatereturnReport] IOException. " + e.getMessage());
		}
	}
	
	
	
}
